#ifndef _SPLINE_2D_H_
#define _SPLINE_2D_H_

#include <vector>
#include "vec2.h"
namespace math
{

class Spline2D
{
public:
	typedef struct _PATH_NODE
	{
		math::Vec2	Pos;
		float		Len;
		void*		pData;
	} PATH_NODE;

	enum SPLINE_TYPE {
		ST_CATMULLROM = 0,
		ST_HERMITE,
		ST_LERP,
	};

	enum GET_METHOD {
		GM_DEFAULT = 0,			// based on actual track length
		GM_NORMALIZED_LOCAL,	// distance between each node is normalised to 1.0
		GM_NORMALIZED_GLOBAL,	// entire track is normalised to 1.0
	};

	Spline2D();
	~Spline2D();

	virtual void			Init();
	virtual void			Clear();

	unsigned int			GetNodeC() const { return (unsigned int)m_Nodes.size(); }
	const SPLINE_TYPE&		GetType() { return m_Type; }
	void					SetType(const SPLINE_TYPE& type) { m_Type = type; }

	const bool				NodeHitTest(const math::Vec2& Pos, int* pOut = NULL, const float& Tolerance = -1.0f);
	const bool				LineHitTest(const math::Vec2& Pos, int* pOut = NULL, math::Vec2* pVOut = NULL, const float& Tolerance = -1.0f);
	const bool				SplineHitTest(const math::Vec2& Pos, float* pOut = NULL, const float& Tolerance = -1.0f);
	const bool				SplineHitTest(const math::Vec2& Pos, math::Vec2* pOut = NULL, const float& Tolerance = -1.0f);

	void					InsertNode(const math::Vec2& Pos);
	void					InsertNode(int Idx, const math::Vec2& Pos);
	void					InsertNode(float s, int* pIdx = NULL);
	void					DeleteNode(int Idx);
	void					SetNodePos(int Idx, const math::Vec2& Pos);
	const bool				GetNodePos(int Idx, math::Vec2& VOut);
	void					SetNodeData(int Idx, void* pData);
	void*					GetNodeData(int Idx);

	math::Vec2				GetPos(const float& Track, const GET_METHOD& Method = GM_DEFAULT);
	const float&			GetTrackLen();

	void					RenderLine();
	void					RenderSpline();

	virtual void			DrawLine(const math::Vec2& From, const math::Vec2& To) {}
	virtual void			DrawNode(const math::Vec2& Pos) {}
	virtual void			DeleteNodeData(PATH_NODE* pNode);

	void					RefreshTrackLen(int Idx);
	math::Vec2				GetPos(int Idx, const float& s);
	void					Interpolate(math::Vec2& VOut,
										const math::Vec2& PV1, const math::Vec2& PV2, const math::Vec2& PV3, const math::Vec2& PV4,
										const float& s);

	unsigned int			GetDrawSegments() const { return DRAW_SEGMENTS; };
	PATH_NODE*				GetNode(int ID) const;
	void					MarkDirty() { m_TrackLen = -1; };
protected:
	unsigned int			MAX_NODES;
	unsigned int			DRAW_SEGMENTS;
	unsigned int			TRACK_SEGMENTS;
	float					ASPECT_RATIO;
	float					NODE_HITTEST_SENSITIVITY;
	float					LINE_HITTEST_SENSITIVITY;

private:
	std::vector<PATH_NODE*>	m_Nodes;
	float					m_TrackLen;

	SPLINE_TYPE				m_Type;
};


class SplineCache2D : public Spline2D
{
public:
	SplineCache2D(const float& interval = 0.001f);
	virtual ~SplineCache2D();

	int						FindCachePos(const math::Vec2& Pos, int From = 0, int To = -1);
	math::Vec2				GetCachePos(int Idx);
	math::Vec2				GetCachePos(int Idx, float Fraction);
	math::Vec2				GetCachePos(float fIdx);
	float					GetCacheS(int Idx);
	float					GetCacheS(float fIdx);

	int 					GetCacheSize();
	void					CacheDirty();

protected:
	void					RefreshCache();

	struct CACHE_POINT {
		math::Vec2	pos;
		float		s;
	};
	CACHE_POINT*			m_pCache;
	int						m_CacheSize;

private:
	float					m_Interval;

	static const int		SUBDIVIDE_INTERVAL = 4;
};

} // namespace math

#endif // _SPLINE_2D_H_
